package com.zhoukx.nio;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.*;
import java.util.Iterator;
import java.util.Set;

/**
 *
 * 案例要求:
 * 编写一个 NIO 入门案例，实现服务器端和客户端之间的数据简单通讯（非阻塞）
 * 目的：理解NIO非阻塞网络编程机制
 */
public class NIOServer {
    public static void main(String[] args) throws IOException {
       // 创建serverSocketChannel ->ServerSocket
        ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();

        //得到一个selecor对象
        Selector selector = Selector.open();

        // 绑定一个端口6666,在服务器端监听
        serverSocketChannel.socket().bind(new InetSocketAddress(6666));

        // 设置为非阻塞
        serverSocketChannel.configureBlocking(false);

        // 把serverSocketChannel注册到selector中
        serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);

        // 循环等待客户端连接
        while (true) {
            // 在这里等待1秒钟,如果没有发生就返回(没有连接事件)
            if (selector.select(1000) == 0) {
                System.out.println("服务器等待一秒钟,无连接");
                continue;
            }
            // 如果返回是大于0 ,则代表有连接,获取想听selectionKey的集合
            //1.如果返回是大于0,表示已经获取到关注事件
            //2.selector.selectedKeys() 表示关注时间的集合
            // 通过selectionKeys返回获取通道
            Set<SelectionKey> selectionKeys = selector.selectedKeys();

            // 这个是获取响应的通道
            Iterator<SelectionKey> iterator = selectionKeys.iterator();


            while (iterator.hasNext()) {

                // 获取一个selection 也是一个管道
                SelectionKey selectionKey = iterator.next();

                // 根据key对应的通道发生的事件作出响应的处理
                // 连接事件处理 // 如果是OP_ACCEPT 代表有新的连接
                if (selectionKey.isAcceptable()) {

                    //该客户生成一个socketChannel
                    SocketChannel socketChannel = serverSocketChannel.accept();

                    System.out.println("客户端连接成功,生成一个socketChannel:"+socketChannel.hashCode());

                    //将socketChannel设置为非阻塞
                    socketChannel.configureBlocking(false);

                    // 将socketChannel注册到selector,关注事件为OP_READ,同时给SocketChannel关联一个buffer
                    socketChannel.register(selector,SelectionKey.OP_READ, ByteBuffer.allocate(1024));

                }

                //SelectionKey.OP_READ  关注事件处理
                if(selectionKey.isReadable()){

                    // 通过key反向获取channel
                    SocketChannel channel = (SocketChannel)selectionKey.channel();

                    // 获取到该channel关联的buffer
                    ByteBuffer attachment = (ByteBuffer)selectionKey.attachment();

                    // 读取buffer
                    channel.read(attachment);

                    System.out.println("form  客户端 " + new String(attachment.array()));

                }
                // 手动从集合中移动当前的selectionKey,房主重复动作
                iterator.remove();
            }

        }
    }
}
